/**
 * This file is part of WiiuseJ.
 *
 *  WiiuseJ is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  WiiuseJ is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with WiiuseJ.  If not, see <http://www.gnu.org/licenses/>.
 */
package wiiusej;

import java.sql.Timestamp;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.Semaphore;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;


import org.apache.commons.collections.CollectionUtils;
import org.apache.log4j.Logger;
import org.apache.log4j.PropertyConfigurator;

import javax.swing.event.EventListenerList;

import sun.security.provider.SHA;

import wiiusej.connection.ApiConnection;
import wiiusej.connection.ConnectionManager;
import wiiusej.connection.ShareConnectionCell;
import wiiusej.wiiusejevents.GenericEvent;
import wiiusej.wiiusejevents.utils.EventsGatherer;
import wiiusej.wiiusejevents.utils.WiiUseApiListener;
import wiiusej.wiiusejevents.wiiuseapievents.StatusEvent;
import wiiusej.wiiusejevents.wiiuseapievents.WiiUseApiEvent;
import wiiusej.wiiusejevents.wiiuseapievents.WiimoteEvent;

/**
 * Class that manages the use of Wiiuse API.
 * 
 * @author guiguito
 */
public class WiiUseApiManager extends Thread implements ApiConnection{

	//Logger 
	private static Logger fileLogger;
	
	//Shared Cell
	private static ShareConnectionCell sharedConnectionCell;
	
	//Packet counter
	private long packetCounter = 0;
	
	
	//Queue for COM with Wiimotes
	BlockingQueue<GenericEvent> eventQueue;
	private static HashMap<Integer, BlockingQueue<GenericEvent>> wiimoteComChannels;
	
	//myID
	private static long myID;
	public static String myName;
	
	private static WiiUseApiManager instance;// = new WiiUseApiManager();

	private final EventListenerList listeners = new EventListenerList();

	private Semaphore semaphore = new Semaphore(0);

	//private Wiimote[] wiimotes;
	
	private static HashMap<Integer, Wiimote> wiimotes;

	private WiiUseApi wiiuse = WiiUseApi.getInstance();

	private int connected = 0;

	private AtomicBoolean running = new AtomicBoolean(false);

	private boolean leave = false;

	public static int WIIUSE_STACK_UNKNOWN = 0;
	public static int WIIUSE_STACK_MS = 1;
	public static int WIIUSE_STACK_BLUESOLEIL = 2;

	//connectionManager
	private static ConnectionManager connectionManager;
	
	//the barrier
	private final CyclicBarrier barrier;
	
	//The lock
	private final Lock lock;
	private final Condition condition;
	
	
	public WiiUseApiManager(ShareConnectionCell cell, CyclicBarrier barrier,
			Lock lock, Condition condition, BlockingQueue<GenericEvent> queue){
		
		instance = this;
		
		
		//WE get the communication object with ConnectionManager object
		this.sharedConnectionCell = cell;
		
		this.eventQueue = queue;
		
		//Logger init
		PropertyConfigurator.configure("log4j.properties");
		fileLogger = Logger.getLogger("wiiuseManager");
		
		//barrier
		this.barrier = barrier;
		
		//the lock
		this.lock = lock;
		this.condition = condition;
		
		
		//ID and Name
		this.myID = this.getId();
		this.myName = this.getName();
		
		fileLogger.debug("Thread Name: " + this.getName() + "ID: " + this.getId());
			
	}
	
	
	public static WiiUseApiManager getInstance() {
		return instance;
	}
	
	/**
	 * Get the number of wiimotes connected.
	 * 
	 * @return the number of wiimotes connected.
	 */
	public static int getNbConnectedWiimotes() {
		return getInstance().connected;
	}

	/**
	 * Stop thread and shutdown wiiuse Api.
	 */
	
	
	public static void shutdown() {
		
		WiiUseApiManager manager = getInstance();
		int pastConnected = manager.connected; 
		
		if(!wiimotes.isEmpty()){
			
			Iterator it = wiimotes.entrySet().iterator();		
			
			while(it.hasNext()){
				Map.Entry entry = (Map.Entry) it.next();
				Wiimote wim = (Wiimote) entry.getValue();
				
				wim.disconnect();
			}			
		}
		
		manager.running.set(false);
		
		if (pastConnected > 0) {
			manager.wiiuse.cleanUp();
		}
		
		/*
		if (manager.connected > 0) {
			for (Wiimote wim : manager.wiimotes) {
				if (wim != null)
					wim.disconnect();
			}
		}
		manager.running.set(false);
		if (pastConnected > 0) {
			manager.wiiuse.cleanUp();
		}*/
	}

	/**
	 * Stop wiiuseJ definitively for this program. It finishes Wiiusej thread
	 * and shutdown wiiuse API.
	 */
	public static void definitiveShutdown() {
		getInstance().leave = true;
		shutdown();
	}

	
	
	/**
	 * Activate the rumble for the wiimote with the given id.
	 * 
	 * @param id
	 *            id of the wiimote.
	 */
	protected void activateRumble(int id) {
		wiiuse.activateRumble(id);
	}

	/**
	 * Deactivate the rumble for the wiimote with the given id.
	 * 
	 * @param id
	 *            id of the wiimote.
	 */
	protected void deactivateRumble(int id) {
		wiiuse.deactivateRumble(id);
	}

	/**
	 * Activate IR Tracking for the wiimote with the given id.
	 * 
	 * @param id
	 *            id of the wiimote.
	 */
	protected void activateIRTRacking(int id) {
		wiiuse.activateIRTracking(id);
	}

	/**
	 * Deactivate IR Tracking for the wiimote with the given id.
	 * 
	 * @param id
	 *            id of the wiimote.
	 */
	protected void deactivateIRTRacking(int id) {
		wiiuse.deactivateIRTracking(id);
	}

	/**
	 * Activate motion sensing for the wiimote with the given id.
	 * 
	 * @param id
	 *            id of the wiimote.
	 */
	protected void activateMotionSensing(int id) {
		wiiuse.activateMotionSensing(id);
	}

	/**
	 * Deactivate motion sensing for the wiimoter with the given id.
	 * 
	 * @param id
	 *            id of the wiimote.
	 */
	protected void deactivateMotionSensing(int id) {
		wiiuse.deactivateMotionSensing(id);
	}

	/**
	 * Activate smoothing the wiimotes with the given id.
	 * 
	 * @param id
	 *            id of the wiimote.
	 */
	protected void activateSmoothing(int id) {
		wiiuse.activateSmoothing(id);
	}

	/**
	 * Deactivate smoothing the wiimotes with the given id.
	 * 
	 * @param id
	 *            id of the wiimote.
	 */
	protected void deactivateSmoothing(int id) {
		wiiuse.deactivateSmoothing(id);
	}

	/**
	 * Activate continuous for the wiimotes with the given id.
	 * 
	 * @param id
	 *            id of the wiimote.
	 */
	protected void activateContinuous(int id) {
		wiiuse.activateContinuous(id);
	}

	/**
	 * Deactivate continuous for the wiimotes with the given id.
	 * 
	 * @param id
	 *            id of the wiimote.
	 */
	protected void deactivateContinuous(int id) {
		wiiuse.deactivateContinuous(id);
	}

	
	public void setLeds(Wiimote wim) {		
		int id = wim.getWiimoteId();
			
		fileLogger.debug("WiimoteLEDS Id: " + id);
		if (id % 4 == 0) {
			wim.setLeds(true, true, true, true);
		} else if (id % 4 == 1) {
			wim.setLeds(true, false, false, false);
		} else if (id % 4 == 2) {
			wim.setLeds(true, true, false, false);
		} else if (id % 4 == 3) {
			wim.setLeds(true, true, true, false);
		}		
	}
	
	/**
	 * Set leds for the wiimotes with the given id.
	 * 
	 * @param id
	 *            id of the wiimote
	 * @param l1
	 *            status of led1. True : ON, False : OFF.
	 * @param l2
	 *            status of led2. True : ON, False : OFF.
	 * @param l3
	 *            status of led3. True : ON, False : OFF.
	 * @param l4
	 *            status of led4. True : ON, False : OFF.
	 */
	protected void setLeds(int id, boolean l1, boolean l2, boolean l3,
			boolean l4) {
		wiiuse.setLeds(id, l1, l2, l3, l4);
	}

	/**
	 * Set the orientation threshold for the given id. (minimum angle between
	 * two events)
	 * 
	 * @param id
	 *            id of the wiimote.
	 * @param th
	 *            threshold in degrees.
	 */
	protected void setOrientationThreshold(int id, float th) {
		wiiuse.setOrientThreshold(id, th);
	}

	/**
	 * Set the acceleration threshold for the given id. (minimum angle between
	 * two events)
	 * 
	 * @param id
	 *            id of the wiimote.
	 * @param th
	 *            threshold.
	 */
	protected void setAccelerationThreshold(int id, int th) {
		wiiuse.setAccelThreshold(id, th);
	}

	/**
	 * Set alpha smoothing for the given id.
	 * 
	 * @param id
	 *            id of the wiimote.
	 * @param th
	 *            threshold.
	 */
	protected void setAlphaSmoothing(int id, float th) {
		wiiuse.setAlphaSmoothing(id, th);
	}

	/**
	 * Try to resync with the wiimote by starting a new handshake.
	 * 
	 * @param id
	 *            id of the wiimote.
	 */
	protected void reSync(int id) {
		wiiuse.reSync(id);
	}

	/**
	 * Set screen aspect ratio to 4/3 for the given id.
	 * 
	 * @param id
	 *            id of the wiimote.
	 */
	protected void setScreenAspectRatio43(int id) {
		wiiuse.setScreenRatio43(id);
	}

	/**
	 * Set screen aspect ratio to 16/9 for the given id.
	 * 
	 * @param id
	 *            id of the wiimote.
	 */
	protected void setScreenAspectRatio169(int id) {
		wiiuse.setScreenRatio169(id);
	}

	/**
	 * Set the sensor bar to be above the screen.
	 * 
	 * @param id
	 *            id of the wiimote.
	 */
	protected void setSensorBarAboveScreen(int id) {
		wiiuse.setSensorBarAboveScreen(id);
	}

	/**
	 * Set the sensor bar to be below the screen.
	 * 
	 * @param id
	 *            id of the wiimote.
	 */
	protected void setSensorBarBelowScreen(int id) {
		wiiuse.setSensorBarBelowScreen(id);
	}

	/**
	 * Set virtual resolution. It is used to automatically compute the position
	 * of a cursor on this virtual screen using the sensor bar. These results
	 * come in the IREvent.
	 * 
	 * @param id
	 *            id of the wiimote.
	 * @param x
	 *            x resolution.
	 * @param y
	 *            y resolution.
	 */
	protected void setVirtualResolution(int id, int x, int y) {
		wiiuse.setVirtualScreenResolution(id, x, y);
	}

	/**
	 * Get Status for the wiimote for the given id.
	 * 
	 * @param id
	 *            id of the wiimote.
	 */
	protected void getStatus(int id) {
		wiiuse.getStatus(id);
	}

	/**
	 * Set the normal and expansion handshake timeouts.
	 * 
	 * @param id
	 *            the id of the wiimote concerned.
	 * @param normalTimeout
	 *            The timeout in milliseconds for a normal read.
	 * @param expansionTimeout
	 *            The timeout in millisecondsd to wait for an expansion
	 *            handshake.
	 */
	protected void setTimeout(int id, short normalTimeout,
			short expansionTimeout) {
		wiiuse.setTimeout(id, normalTimeout, expansionTimeout);
	}

	/**
	 * Set the IR sensitivity.
	 * 
	 * @param id
	 *            the id of the wiimote concerned.
	 * @param level
	 *            1-5, same as Wii system sensitivity setting. If the level is <
	 *            1, then level will be set to 1. If the level is > 5, then
	 *            level will be set to 5.
	 */
	protected void setIrSensitivity(int id, int level) {
		wiiuse.setIrSensitivity(id, level);
	}

	/**
	 * Set the nunchuk orientation threshold for the given id. (minimum angle
	 * between two events)
	 * 
	 * @param id
	 *            id of the wiimote.
	 * @param th
	 *            threshold in degrees.
	 */
	protected void setNunchukOrientationThreshold(int id, float th) {
		wiiuse.setNunchukOrientationThreshold(id, th);
	}

	/**
	 * Set the nunchuk acceleration threshold for the given id. (minimum angle
	 * between two events)
	 * 
	 * @param id
	 *            id of the wiimote.
	 * @param th
	 *            threshold.
	 */
	protected void setNunchukAccelerationThreshold(int id, int th) {
		wiiuse.setNunchukAccelerationThreshold(id, th);
	}

	/**
	 * Force the bluetooth stack type.(useful only for windows)
	 * 
	 * @param type
	 *            must be WIIUSE_STACK_UNKNOWN or WIIUSE_STACK_MS or
	 *            WIIUSE_STACK_BLUESOLEIL.
	 */
	public void setBlueToothstackType(int type) {
		wiiuse.windowsSetBluetoothStack(type);
	}

	
	@SuppressWarnings("unchecked")
	private HashMap<Integer, BlockingQueue<GenericEvent>> uploadConnectionChanges(){
		
		return sharedConnectionCell.getAllWiimotes(myName + "(" + myID + ")");
		/*
		//we upload our wiimote structure
		HashMap<Integer, Wiimote> uploadedWiimotes = sharedConnectionCell.
			getAllWiimotes(myName + "(" + myID + ")");
		
		if(! uploadedWiimotes.equals(wiimotes)){
			//There were changes
			
			Set newSet = uploadedWiimotes.entrySet();
			Set oldSet = wiimotes.entrySet();
			
			Set intersection = new HashSet(newSet);
		    intersection.retainAll(oldSet);
		       
		    //Elements to remove
		    Set toDelete = new HashSet(oldSet);
		    toDelete.removeAll(intersection);
			
		    //Elements to add
		    Set toAdd = new HashSet(newSet);
		    toAdd.removeAll(intersection);
		    		    
		    
		    if(!toDelete.isEmpty()){
		      	//Delete the disconnected wiimotes from wiimote and listener data structure
			    HashMap<Integer, Wiimote> toDeleteWiimotes = (HashMap<Integer, Wiimote>) toDelete;
			    removeSetOfWiiUseApiListeners(toDeleteWiimotes);
			    removeSetOfWiimotes(toDeleteWiimotes);			    
		    }
		    
		    if(!toAdd.isEmpty()){
		    	//Add to wiimote and listener datastructure the new found devices
			    addSetOfWiiUseApiListeners((HashMap<Integer, Wiimote>) toAdd);
			    addSetOfWiimotes((HashMap<Integer, Wiimote>) toAdd);		    	
		    }  
		    
		    fileLogger.debug("Uploading --> Added Devices: " + 
		    		toAdd + "Removed Devices: " + toDelete);
		
		    return (HashMap<Integer, Wiimote>) toAdd;
		}
		
		return null;*/
	}
	@Override
	public void run() {

		//First of all we try to get the wiimotes from the shared cell.
		//We wait until we have any device connected
		
		fileLogger.debug("RUN METHOD");
		
		//wait until the lock is released by other thread
		fileLogger.debug("Getting the LOCK");
		lock.lock();
				
		try{
			while(sharedConnectionCell.getAllWiimotes(myName + "(" + myID + ")") == null){
				fileLogger.debug("Waiting for CONDITION");
				condition.await();				
			}
			
		}catch(Exception e){
			e.printStackTrace();
		}finally{			
			fileLogger.debug("Releasing the LOCK. SIGNALED");
			lock.unlock();			
		}		

		wiimoteComChannels = sharedConnectionCell.getAllWiimotes(myName + "(" + myID + ")");
		this.connected = wiimoteComChannels.size();
		
		
		fileLogger.debug("Found Connected Channels: " + wiimoteComChannels + 
				"Connected: " + this.connected);
		
		/*
		if(!wiimotes.isEmpty()){
			//We have to register the wiimotes as a WiiUseApiManager Listeners.
			addSetOfWiiUseApiListeners(wiimotes);				
			fileLogger.debug("Listeners: " + listeners);
		}*/
		
		running.set(true);		
		
		EventsGatherer gather = new EventsGatherer(connected);	
		
		while (running.get() && !wiimoteComChannels.isEmpty()) {
						
			//Check if there were changes in the wiimote connection
			
			HashMap<Integer, Wiimote> uploadedWiimotes;
			
			/*
			if( (uploadedWiimotes = uploadConnectionChanges()) != null){				
				addSetOfWiiUseApiListeners(wiimotes);				
			}*/						
			
			//EVENT POLLING
			wiiuse.specialPoll(gather);
						
			
			/* deal with events gathered in Wiiuse API */
			for (WiiUseApiEvent evt : gather.getEvents()) {
				if (evt.getWiimoteId() != -1) {// event filled
					
					//fileLogger.debug("EVENT GATHER TYPE: " + evt.getEventType() + "From: " + 
							//evt.getWiimoteId());
					// there is an event notify observers
					
					if(evt.getEventType() != WiiUseApiEvent.DISCONNECTION_EVENT){						
						notifyWiiUseApiListener(evt);
					}
					
					
					if (evt.getEventType() == WiiUseApiEvent.DISCONNECTION_EVENT) {
						//here we have to call to the Connection Manager through shared cell
						//sharedConnectionCell.setEvent(evt, myName + "(" + myID + ")");
						
						//send the notification to the connectionManager
						connectionManager.onWiiUseApiEvent(evt);
					}
				} else {
					/*System.out
							.println("There is an event with id == -1 ??? there is a problem !!! : "
									+ evt);*/
				}
			}
			gather.clearEvents();
		}		
		
	}
	
	
	private void waitUntilConnectedWiimotes(){
		
		//wait until get the lock
		lock.lock();
		
		fileLogger.debug("Getting the lock");
		
		try{
			fileLogger.debug("Wait for condition");
			while(sharedConnectionCell.getAllWiimotes(myName + "(" + myID + ")") != null){
				condition.await();
			}
					
			fileLogger.debug("Signal Received for condition");
		} catch (InterruptedException e) {
			
			e.printStackTrace();
		}finally{
			fileLogger.debug("Releasing the LOCK");
			lock.unlock();			
		}
		
	}

	private void addSetOfWiimotes(HashMap<Integer, Wiimote> wiimotes){
		
		Iterator it = wiimotes.entrySet().iterator();		
		
		while(it.hasNext()){
			Map.Entry entry = (Map.Entry) it.next();
			Wiimote wim = (Wiimote) entry.getValue();
			
			this.wiimotes.putAll(wiimotes);
		}
		
		
	}
	
	private void addSetOfWiiUseApiListeners(HashMap<Integer, Wiimote> listeners){
		
		Iterator it = listeners.entrySet().iterator();		
		
		while(it.hasNext()){
			Map.Entry entry = (Map.Entry) it.next();
			Wiimote wim = (Wiimote) entry.getValue();
			
			addWiiUseApiListener(wim);
		}
		
		
		
	}
	
	/**
	 * Add WiiUseApiListener to the listeners list.
	 * 
	 * @param listener
	 *            a WiiUseApiListener
	 */
	public void addWiiUseApiListener(WiiUseApiListener listener) {
		listeners.add(WiiUseApiListener.class, listener);
		fileLogger.debug("ConnectedBefore: " + this.connected);
		this.connected++;
		fileLogger.debug("ConnectedAfter: " + this.connected);
	}
	
	private void removeSetOfWiimotes(HashMap<Integer, Wiimote> wiimotes){
		
		Iterator it = wiimotes.entrySet().iterator();		
		
		while(it.hasNext()){
			Map.Entry entry = (Map.Entry) it.next();
			Wiimote wim = (Wiimote) entry.getValue();			
			removeWiimote(wim);
		}
		
	}

	
	private void removeWiimote(Wiimote wim) {
		this.wiimotes.remove(wim);		
	}


	private void removeSetOfWiiUseApiListeners(HashMap<Integer, Wiimote> listeners){
		
		Iterator it = listeners.entrySet().iterator();		
		
		while(it.hasNext()){
			Map.Entry entry = (Map.Entry) it.next();
			Wiimote wim = (Wiimote) entry.getValue();			
			removeWiiUseApiListener(wim);
		}
		
	}
	
	/**
	 * Remove WiiUseApiListener from the listeners list.
	 * 
	 * @param listener
	 *            a WiiUseApiListener
	 */
	protected void removeWiiUseApiListener(WiiUseApiListener listener) {
		listeners.remove(WiiUseApiListener.class, listener);
		this.connected--;
	}

	/**
	 * Get the list of WiiUseApiListeners.
	 * 
	 * @return the list of WiiUseApiListeners.
	 */
	protected WiiUseApiListener[] getWiiUseApiListeners() {
		return listeners.getListeners(WiiUseApiListener.class);
	}

	/**
	 * Notify WiiUseApiListeners that an event occured.
	 * 
	 * @param evt
	 *            GenericEvent occured
	 */
	private void notifyWiiUseApiListener(WiiUseApiEvent evt) {
		
		
		if (evt.getEventType() == WiiUseApiEvent.GENERIC_EVENT) {
			
			HashMap<String, Timestamp> timeStamp;		
			Timestamp ts = new Timestamp (System.currentTimeMillis());
			//timeStamp = ((WiimoteEvent) evt).getMotionSensingEvent().getTimeStamp();
			
			timeStamp = new HashMap<String, Timestamp>();
			timeStamp.put(myName,ts);
			
			((WiimoteEvent) evt).getMotionSensingEvent().setTimeStamp(timeStamp);
			((WiimoteEvent) evt).getMotionSensingEvent().setTime(ts);
			
			((WiimoteEvent) evt).setTime(ts);
			
			//set refNumber			
			packetCounter++;
			evt.setRefNumber(packetCounter);
		}
		
		BlockingQueue<GenericEvent> q = wiimoteComChannels.get(evt.getWiimoteId());
		
		if( q !=  null){
			//the data channel exists
			
			if((evt instanceof WiimoteEvent)){
				fileLogger.info("Packet: " + evt.getRefNumber() + 
						" Sending Event To: " + evt.getWiimoteId() +
						" Time: " + ((WiimoteEvent) evt).getTime());
				}
			
			try {				
				q.put(evt);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}			
			
		}else{
			fileLogger.info("ERROR finding comunucation channel. Wiimote: " + evt.getWiimoteId() + 
					" does NOT exists");
		}
		
		
		
		
		
		
		
		/*
		//we should just notify the listener which is listening the device which triggered the 
		//event
		
		for (WiiUseApiListener listener : getWiiUseApiListeners()) {			
			Wiimote wiimote = (Wiimote) listener;
			
			if(wiimote.getId() == evt.getWiimoteId()){
				
				if((evt instanceof WiimoteEvent)){
					fileLogger.info("Sending Event: " + evt.getEventType() + " From: " + evt.getWiimoteId() + 
							" To: " + wiimote.getId() + " Time: " + ((WiimoteEvent) evt).getTime());					
				
				}else if( (evt instanceof StatusEvent) ){
					fileLogger.info("Sending Event: " + evt.getEventType() + " To: " + wiimote.getId()
							+ "StatusEvent");	
					
				}
				
				
				listener.onWiiUseApiEvent(evt);
			}
			
		}*/
		
		
	}

	/**
	 * Called by the garbage collector at the end.
	 */
	protected void finalize() throws Throwable {
		shutdown();
	}
	
	
	/***************************************************
	MOD2: SPEAKER BASIC METHODS
	***************************************************/
	protected void activateSpeaker(int id){
		wiiuse.activateSpeaker(id);
	}
	
	protected void sendSound(int id){
		wiiuse.sendSound(id);		
	}
	
	protected void deactivateSpeaker(int id){
		wiiuse.deactivateSpeaker(id);		
	}
	
	/***************************************************
	MOD3: SPEAKER VOLUME METHODS
	***************************************************/
	
	protected int turnVolumeUp(int id){
		return wiiuse.turnVolumeUp(id);
	}
	
	protected int turnVolumeDown(int id){
		return wiiuse.turnVolumeDown(id);
	}
	
	protected int getVolumeLevel(int id){
		return wiiuse.getVolumeLevel(id);
	}
	
	/***************************************************
	MOD4: SPEAKER CONFIG METHODS
	***************************************************/

	protected void activateSpeaker(int id, int config, byte[] sampleRate){
		wiiuse.activateSpeaker(id, config, sampleRate);
	}
	
	
	
	/***************************************************
	API CONECTION INTERFACE IMPLEMENTATION
	***************************************************/
	
	
	@Override
	public void initDataStructure(int nbPossibleWiimotes) {
		wiiuse.init(nbPossibleWiimotes);
		
	}

	@Override
	public int findWiimotes(int nbMaxWiimotes, int timeout) {		
		return wiiuse.find(nbMaxWiimotes, timeout);
	}

	@Override
	public int connectDiscoveredWiimotes(int nbWiimotesFound) {		
		return wiiuse.connect(nbWiimotesFound);
	}

	@Override
	public int getUnId(int i) {
		return wiiuse.getUnId(i);
	}
	
	
	public HashMap<Integer, Wiimote> getWiimotes(){		
		return wiimotes;
	}
	
	@Override
	public void closeSetOfConnections(HashMap<Integer, Wiimote> toDelete){
		
		Iterator it = toDelete.entrySet().iterator();		
		
		while(it.hasNext()){
			Map.Entry entry = (Map.Entry) it.next();
			Wiimote aux = (Wiimote) entry.getValue();
			
			closeConnection(aux.getWiimoteId());
		}
		
	}
	
	
	/**
	 * Ask the thread to close a connection.
	 * 
	 * @param id
	 *            id of the wiimote to disconnect.
	 */
	
	@Override
	public void closeConnection(int id) {
		if(wiimotes.containsKey(id)){			
			
			//Remove it from listeners list
			//removeWiiUseApiListener(wiimotes.get(id));
			
			//Remove it from wiimote object list
			//wiimotes.remove(id);
			
			//Close connection in wiiuse 
			wiiuse.closeConnection(id);
		}
		
		if(wiimotes.isEmpty()){
			running.set(false);			
		}
	}

	public void setConnectionManager(ConnectionManager connectionManager){
		WiiUseApiManager.connectionManager = connectionManager;		
	}
	
	
	public void activateRumble(Wiimote wim){		
		try {
			activateRumble(wim.getWiimoteId());
			sleep(500);
		} catch (InterruptedException e) {
		}
		finally{
			deactivateRumble(wim.getWiimoteId());
		}		
		
	}
	
}

